package main

import (
	"encoding/json"
	"log"
	"os"
	"path"
	"path/filepath"
	"sync"

	"github.com/tealeg/xlsx"
)

///////////////////////////////////////////////////////
var conf Conf

type Conf struct {
	SrcDir string `json:"src"`
	DstDir string `json:"dst"`
	SrcG   int    `json:"srcg"`
	DstG   int    `json:"dstg"`
}

///////////////////////////////////////////////////////
func Walk(p string) []string {
	l := []string{}
	if err := filepath.Walk(p, func(p string, f os.FileInfo, err error) error {
		if f != nil && !f.IsDir() && path.Ext(p) == ".xlsx" {
			l = append(l, f.Name())
		}
		return nil
	}); err != nil {
		panic(err)
	}
	return l
}

///////////////////////////////////////////////////////

type DictInfo struct {
	Src   string
	Dst   string
	Dict  map[string][]*xlsx.Cell
	Excel *xlsx.File
}

var (
	makeG  = 1
	makeC  = make(chan *DictInfo, 1000)
	makeWG = sync.WaitGroup{}

	mergeG  = 1
	mergeC  = make(chan *DictInfo, 1000)
	mergeWG = sync.WaitGroup{}

	saveG  = 1
	saveC  = make(chan *DictInfo, 1000)
	saveWG = sync.WaitGroup{}

	defaultStyle = xlsx.NewStyle()
)

func Merge(f string) {
	log.Println("merge file:", f)

	info := &DictInfo{
		Src:  conf.SrcDir + f,
		Dst:  conf.DstDir + f,
		Dict: make(map[string][]*xlsx.Cell, 32),
	}

	makeC <- info
}

func MakeDict() {
	makeWG.Add(1)
	go func() {
		defer func() {
			makeWG.Done()
		}()
		for info := range makeC {
			excel, err := xlsx.OpenFile(info.Src)
			if err != nil {
				panic(err)
			}

			for _, sheet := range excel.Sheets {
				for _, row := range sheet.Rows {
					if len(row.Cells) > 1 {
						v := []*xlsx.Cell{}
						for _, c := range row.Cells[1:] {
							if c.String() != "" {
								v = append(v, c)
							}
						}
						info.Dict[row.Cells[0].String()] = v
						//info.Dict[row.Cells[0].String()] = row.Cells[1:]
					}
				}
			}
			mergeC <- info
		}
	}()
}

func MergeDict() {
	mergeWG.Add(1)
	go func() {
		defer mergeWG.Done()
		for info := range mergeC {
			excel, err := xlsx.OpenFile(info.Dst)
			if err != nil {
				panic(err)
			}

			change := false

			for _, sheet := range excel.Sheets {
				for _, row := range sheet.Rows {
					if cells, ok := info.Dict[row.Cells[0].String()]; ok {
						// just copy new cell
						for i := 0; i < len(cells); i++ {
							if len(row.Cells)-1 > i && row.Cells[i+1].String() == "" {
								*row.Cells[i+1] = *cells[i]
								change = true
							} else if len(row.Cells)-1 <= i {
								cell := row.AddCell()
								*cell = *cells[i]
								change = true
							}
						}
					}
				}
			}

			if change {
				info.Excel = excel
				saveC <- info
			}
		}
	}()
}

func SaveDict() {
	saveWG.Add(1)
	go func() {
		defer saveWG.Done()
		for info := range saveC {
			// clear style
			for _, sheet := range info.Excel.Sheets {
				for _, row := range sheet.Rows {
					for _, cell := range row.Cells {
						cell.SetStyle(defaultStyle)
					}
				}
				for _, col := range sheet.Cols {
					col.SetStyle(defaultStyle)
				}
			}
			x := &xlsx.File{
				Sheet:  info.Excel.Sheet,
				Sheets: info.Excel.Sheets,
			}
			x.Save(info.Dst)
		}
	}()
}

func init() {
	f, err := os.Open("./conf.json")
	if err != nil {
		panic(err)
	}
	err = json.NewDecoder(f).Decode(&conf)
	if err != nil {
		panic(err)
	}
}

func main() {
	for i := 0; i < makeG; i++ {
		MakeDict()
	}
	for i := 0; i < mergeG; i++ {
		MergeDict()
	}
	for i := 0; i < saveG; i++ {
		SaveDict()
	}

	lf := Walk(conf.SrcDir)
	for _, f := range lf {
		Merge(f)
	}

	close(makeC)
	makeWG.Wait()
	close(mergeC)
	mergeWG.Wait()
	close(saveC)
	saveWG.Wait()
}
